﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Oryon.Extensions.Reflection
{
    /// <summary>
    /// Extension methods for Reflection operations
    /// </summary>
    public static class Functions
    {
        /// <summary>
        /// Return a property value from object instance at runtime
        /// </summary>
        /// <typeparam name="T">Type of object instance</typeparam>
        /// <param name="source">object instance</param>
        /// <param name="propertyName">Property name</param>
        /// <returns>if property found than it returns property' s value else it returns null</returns>
        public static object GetRuntimeValue<T>(this T source, string propertyName)
        {
            object propertyValue = null;

            Type type = source.GetType();
            var property = (from p in type.GetProperties()
                            where p.Name.StartsWith(propertyName)
                            select p).FirstOrDefault();

            if (property != null)
                propertyValue = property.GetValue(source, null);

            return propertyValue;
        }

        /// <summary>
        /// Call any type instance's method on runtime
        /// </summary>
        /// <typeparam name="T">instance type</typeparam>
        /// <param name="source">object instance</param>
        /// <param name="methodName">method name that calling on runtime</param>
        /// <param name="parameters">method parameters</param>
        /// <returns></returns>
        public static object Call<T>(this T source, string methodName, params object[] parameters)
        {
            object callResult = null;

            Type type = source.GetType();
            var methodInfo = (from m in type.GetMethods()
                              where m.Name.StartsWith(methodName)
                              select m).FirstOrDefault();
            if (methodInfo != null)
                callResult = methodInfo.Invoke(source, parameters);

            return callResult;
        }

        /// <summary>
        /// Return the non primitive property list of an source object
        /// </summary>
        /// <typeparam name="T">source object type</typeparam>
        /// <param name="source">source object instance</param>
        /// <returns>it returns non primitive property list</returns>
        public static IEnumerable<TypePropertyInfo> GetNoneprimitiveProperties<T>(this T source)
        {
            IEnumerable<TypePropertyInfo> result = null;
            try
            {
                var assembly = Assembly.GetAssembly(typeof(T));
                var typeList = assembly.GetTypes();
                var noneprimitiveProperties = from t in typeList
                                              from p in t.GetProperties()
                                              where !p.ReflectedType.IsPrimitive
                                              select new TypePropertyInfo
                                              {
                                                  PropertyName=p.Name,
                                                  TypeName=p.ReflectedType.Name,
                                                  Container=assembly
                                              };
                result=noneprimitiveProperties;
            }
            catch 
            {
                
            }
            return result;
        }

        /// <summary>
        /// Create ant type object from container assembly
        /// </summary>
        /// <param name="assemblyPath">Assembly File path</param>
        /// <param name="className">Class name</param>
        /// <param name="arguments">Arguments for creating class</param>
        /// <returns>it returns object instance with starting arguments</returns>
        public static object CreateObject(this string assemblyPath, string className, params object[] arguments)
        {
            object instance = null;

            if (File.Exists(assemblyPath))
            {
                Assembly assembly = Assembly.LoadFrom(assemblyPath);
                Type candidateType = assembly.GetTypes().Where(t => t.Name == className).FirstOrDefault();
                if (candidateType != null)
                    instance = Activator.CreateInstance(candidateType, arguments);
            }

            return instance;
        }
    }
}
